package com.example.demo.exception;

import com.example.demo.domain.dto.Result;
import com.example.demo.domain.dto.ResultCode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.example.demo.constant.CommonConstant.LOG_TRACE_ID;

/**
 * 全局异常处理
 *
 * @author renliang
 */
@Slf4j
@RestControllerAdvice
@ConditionalOnWebApplication
public class GlobalExceptionHandler {
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public Result<?> bindExceptionHandler(BindException e) {
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return Result.error(message, MDC.get(LOG_TRACE_ID));
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        return Result.error(message, MDC.get(LOG_TRACE_ID));
    }

    /**
     * 校验异常
     */
    @ExceptionHandler(ValidationException.class)
    public Result<?> validationExceptionHandler(ValidationException e) {
        String message = e.getMessage();
        return Result.error(message, MDC.get(LOG_TRACE_ID));
    }


    @ExceptionHandler(FileSizeLimitExceededException.class)
    public Result<?> fileSizeLimitExceededException(FileSizeLimitExceededException e) {
        long actualSize = e.getActualSize() / 1024 / 1024;
        long permittedSize = e.getPermittedSize() / 1024 / 1024;
        log.error("文件{} M 大小超过最大限制最大{} M", actualSize, permittedSize);
        return Result.warn("文件大小" + actualSize + " M，超过最大限制" + permittedSize + "M", MDC.get(LOG_TRACE_ID));
    }

    /**
     * 运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result<?> runtimeExceptionHandler(RuntimeException e) {
        e.printStackTrace();
        return Result.error(ResultCode.ERROR.getMsg(), MDC.get(LOG_TRACE_ID));
    }

    /**
     * 其他异常
     */
    @ExceptionHandler(Exception.class)
    public Result<?> exceptionHandler(Exception e) {
        e.printStackTrace();
        return Result.error(ResultCode.ERROR.getMsg(), MDC.get(LOG_TRACE_ID));
    }

    /**
     * 运行时异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> illegalArgumentException(IllegalArgumentException e) {
        return Result.error(ResultCode.ERROR, e.getMessage(), MDC.get(LOG_TRACE_ID));
    }

    /**
     * 捕获参数类型不匹配导致的异常
     *
     * @param e 参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<?> handleTypeMismatch(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String url = request.getRequestURI();
        String name = e.getName();
        Object value = e.getValue();
        Class<?> requiredType = e.getRequiredType();
        String type = "";
        if (requiredType != null) {
            type = requiredType.getSimpleName();
        }
        log.error("请求参数异常 ：request url {} ,{} should be a valid {} and {} isn't", url, name, type, value);
        return Result.error(ResultCode.ERROR, String.format("请求参数%s类型不匹配", name), MDC.get(LOG_TRACE_ID));
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<?> handleMediaTypeNotSupported(HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
        String url = request.getRequestURI();
        MediaType mediaType = e.getContentType();
        String contentType = "";
        if (Objects.nonNull(mediaType)) {
            contentType = mediaType.getType();
        }
        List<MediaType> list = e.getSupportedMediaTypes();
        Set<String> set = list.stream().map(MediaType::getType).collect(Collectors.toSet());
        log.error("请求媒体类型{}不支持异常，支持的媒体类型{} ：request url {} ", contentType, String.join(",", set), url);
        return Result.error(ResultCode.ERROR, String.format("请求媒体类型%s不被支持", contentType), MDC.get(LOG_TRACE_ID));
    }

    /**
     * 自定义异常
     *
     * @see CustomException
     */
    @ExceptionHandler(CustomException.class)
    public Result<?> customRuntimeExceptionHandler(CustomException e) {
        if (Objects.nonNull(e.getResult())) {
            ResultCode resultCode = e.getResult();
            return Result.error(resultCode, MDC.get(LOG_TRACE_ID));
        }
        return Result.error(ResultCode.WARN, e.getMessage(), MDC.get(LOG_TRACE_ID));
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<?> handleMissingServletRequestParameter(MissingServletRequestParameterException e, HttpServletRequest request) {
        String url = request.getRequestURI();
        String name = e.getParameterName();
        log.error("请求参数异常 ：request url {} ,请求参数 {} 不能为空", url, name);
        return Result.error(ResultCode.ERROR, String.format("请求参数%s不能为空", name), MDC.get(LOG_TRACE_ID));
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        e.printStackTrace();
        return Result.error(ResultCode.ERROR, "请求参数格式错误", MDC.get(LOG_TRACE_ID));
    }

    /**
     * 捕获不支持的请求方法类型异常
     *
     * @param e 不支持的请求方法
     * @return 响应
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String url = request.getRequestURI();
        String[] supportedMethods = e.getSupportedMethods();
        String method = e.getMethod();
        String supportedMethodsStr = "";
        if (supportedMethods != null) {
            supportedMethodsStr = StringUtils.arrayToDelimitedString(supportedMethods, ", ");
        }
        log.error("不支持的请求方法类型：request url {} ,supportedMethods {}, request method {}", url, supportedMethodsStr, method);
        return Result.error("不支持的请求方法类型", MDC.get(LOG_TRACE_ID));
    }
}
